use std::{
	ffi::{CStr, CString},
	fmt,
	marker::PhantomData,
	ptr,
	str::from_utf8_unchecked,
};

use super::{Iter, Owned};
use crate::ffi::*;

pub struct Ref<'a> {
	ptr: *const AVDictionary,

	_marker: PhantomData<&'a ()>,
}

impl<'a> Ref<'a> {
	pub unsafe fn wrap(ptr: *const AVDictionary) -> Self {
		Ref {
			ptr,
			_marker: PhantomData,
		}
	}

	pub unsafe fn as_ptr(&self) -> *const AVDictionary {
		self.ptr
	}
}

impl<'a> Ref<'a> {
	pub fn get(&'a self, key: &str) -> Option<&'a str> {
		unsafe {
			let key = CString::new(key).unwrap();
			let entry = av_dict_get(self.as_ptr(), key.as_ptr(), ptr::null_mut(), 0);

			if entry.is_null() {
				None
			}
			else {
				Some(from_utf8_unchecked(CStr::from_ptr((*entry).value).to_bytes()))
			}
		}
	}

	pub fn len(&self) -> usize {
		unsafe { av_dict_count(self.as_ptr()) as usize }
	}

	pub fn iter(&self) -> Iter<'_> {
		unsafe { Iter::new(self.as_ptr()) }
	}

	pub fn to_owned(&self) -> Owned {
		self.iter().collect()
	}
}

impl<'a> IntoIterator for &'a Ref<'a> {
	type IntoIter = Iter<'a>;
	type Item = (&'a str, &'a str);

	fn into_iter(self) -> Self::IntoIter {
		self.iter()
	}
}

impl<'a> fmt::Debug for Ref<'a> {
	fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
		fmt.debug_map().entries(self.iter()).finish()
	}
}
